home *** CD-ROM | disk | FTP | other *** search
/ PC World Komputer 2010 April / PCWorld0410.iso / hity wydania / Ubuntu 9.10 PL / karmelkowy-koliberek-desktop-9.10-i386-PL.iso / casper / filesystem.squashfs / usr / share / pyshared / DistUpgrade / DistUpgradeCache.py < prev    next >
Text File  |  2009-11-02  |  48KB  |  1,102 lines

  1. # DistUpgradeCache.py 
  2. #  
  3. #  Copyright (c) 2004-2008 Canonical
  4. #  
  5. #  Author: Michael Vogt <michael.vogt@ubuntu.com>
  6. #  This program is free software; you can redistribute it and/or 
  7. #  modify it under the terms of the GNU General Public License as 
  8. #  published by the Free Software Foundation; either version 2 of the
  9. #  License, or (at your option) any later version.
  10. #  This program is distributed in the hope that it will be useful,
  11. #  but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. #  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  13. #  GNU General Public License for more details.
  14. #  You should have received a copy of the GNU General Public License
  15. #  along with this program; if not, write to the Free Software
  16. #  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
  17. #  USA
  18.  
  19. import warnings
  20. warnings.filterwarnings("ignore", "apt API not stable yet", FutureWarning)
  21. import apt
  22. import apt_pkg
  23. import os
  24. import os.path
  25. import re
  26. import logging
  27. import string
  28. import statvfs
  29. import time
  30. import gettext
  31. import datetime
  32. import threading
  33. import ConfigParser
  34. from subprocess import Popen, PIPE
  35.  
  36. from DistUpgradeGettext import gettext as _
  37. from DistUpgradeGettext import ngettext
  38. from DistUpgradeConfigParser import DistUpgradeConfig
  39. from DistUpgradeView import FuzzyTimeToStr
  40.  
  41. class CacheException(Exception):
  42.     pass
  43. class CacheExceptionLockingFailed(CacheException):
  44.     pass
  45. class CacheExceptionDpkgInterrupted(CacheException):
  46.     pass
  47.  
  48. # the initrd/vmlinuz/abi space required in /boot for each kernel
  49. KERNEL_INITRD_SIZE = 15*1024*1024
  50.  
  51. class FreeSpaceRequired(object):
  52.     """ FreeSpaceRequired object:
  53.     
  54.     This exposes:
  55.     - the total size required (size_total)
  56.     - the dir that requires the space (dir)
  57.     - the additional space that is needed (size_needed)
  58.     """
  59.     def __init__(self, size_total, dir, size_needed):
  60.         self.size_total = size_total
  61.         self.dir = dir
  62.         self.size_needed = size_needed
  63.     def __str__(self):
  64.         return "FreeSpaceRequired Object: Dir: %s size_total: %s size_needed: %s" % (self.dir, self.size_total, self.size_needed)
  65.     
  66.  
  67. class NotEnoughFreeSpaceError(CacheException):
  68.     """ 
  69.     Exception if there is not enough free space for this operation 
  70.     
  71.     """
  72.     def __init__(self, free_space_required_list):
  73.         self.free_space_required_list = free_space_required_list
  74.  
  75. class MyCache(apt.Cache):
  76.     ReInstReq = 1
  77.     HoldReInstReq = 3
  78.  
  79.     # init
  80.     def __init__(self, config, view, quirks, progress=None, lock=True):
  81.         apt.Cache.__init__(self, progress)
  82.         self.to_install = []
  83.         self.to_remove = []
  84.         self.view = view
  85.         self.quirks = quirks
  86.         self.lock = False
  87.         self.partialUpgrade = False
  88.         self.config = config
  89.         self.metapkgs = self.config.getlist("Distro","MetaPkgs")
  90.         # acquire lock
  91.         self._listsLock = -1
  92.         if lock:
  93.             try:
  94.                 apt_pkg.PkgSystemLock()
  95.                 self.lockListsDir()
  96.                 self.lock = True
  97.             except SystemError, e:
  98.                 # checking for this is ok, its not translatable
  99.                 if "dpkg --configure -a" in str(e):
  100.                     raise CacheExceptionDpkgInterrupted, e
  101.                 raise CacheExceptionLockingFailed, e
  102.         # a list of regexp that are not allowed to be removed
  103.         self.removal_blacklist = config.getListFromFile("Distro","RemovalBlacklistFile")
  104.         self.uname = Popen(["uname","-r"],stdout=PIPE).communicate()[0].strip()
  105.         self._initAptLog()
  106.         # from hardy on we use recommends by default, so for the 
  107.         # transition to the new dist we need to enable them now
  108.         if (config.get("Sources","From") == "hardy" and 
  109.             not "RELEASE_UPGRADE_NO_RECOMMENDS" in os.environ):
  110.             apt_pkg.Config.Set("APT::Install-Recommends","true")
  111.  
  112.     @property
  113.     def reqReinstallPkgs(self):
  114.         " return the packages not downloadable packages in reqreinst state "
  115.         reqreinst = set()
  116.         for pkg in self:
  117.             if (not pkg.candidateDownloadable and 
  118.                 (pkg._pkg.InstState == self.ReInstReq or
  119.                  pkg._pkg.InstState == self.HoldReInstReq)):
  120.                 reqreinst.add(pkg.name)
  121.         return reqreinst
  122.  
  123.     def fixReqReinst(self, view):
  124.         " check for reqreinst state and offer to fix it "
  125.         reqreinst = self.reqReinstallPkgs
  126.         if len(reqreinst) > 0:
  127.             header = ngettext("Remove package in bad state",
  128.                               "Remove packages in bad state", 
  129.                               len(reqreinst))
  130.             summary = ngettext("The package '%s' is in an inconsistent "
  131.                                "state and needs to be reinstalled, but "
  132.                                "no archive can be found for it. "
  133.                                "Do you want to remove this package "
  134.                                "now to continue?",
  135.                                "The packages '%s' are in an inconsistent "
  136.                                "state and need to be reinstalled, but "
  137.                                "no archives can be found for them. Do you "
  138.                                "want to remove these packages now to "
  139.                                "continue?",
  140.                                len(reqreinst)) % ", ".join(reqreinst)
  141.             if view.askYesNoQuestion(header, summary):
  142.                 self.releaseLock()
  143.                 cmd = ["dpkg","--remove","--force-remove-reinstreq"] + list(reqreinst)
  144.                 view.getTerminal().call(cmd)
  145.                 self.getLock()
  146.                 return True
  147.         return False
  148.  
  149.     # logging stuff
  150.     def _initAptLog(self):
  151.         " init logging, create log file"
  152.         logdir = self.config.getWithDefault("Files","LogDir",
  153.                                             "/var/log/dist-upgrade")
  154.         if not os.path.exists(logdir):
  155.             os.makedirs(logdir)
  156.         apt_pkg.Config.Set("Dir::Log",logdir)
  157.         apt_pkg.Config.Set("Dir::Log::Terminal","apt-term.log")
  158.         self.logfd = os.open(os.path.join(logdir,"apt.log"),
  159.                              os.O_RDWR|os.O_CREAT|os.O_APPEND|os.O_SYNC, 0644)
  160.         os.write(self.logfd, "Log time: %s\n" % datetime.datetime.now())
  161.         # turn on debugging in the cache
  162.         apt_pkg.Config.Set("Debug::pkgProblemResolver","true")
  163.         apt_pkg.Config.Set("Debug::pkgDepCache::AutoInstall","true")
  164.     def _startAptResolverLog(self):
  165.         if hasattr(self, "old_stdout"):
  166.             os.close(self.old_stdout)
  167.             os.close(self.old_stderr)
  168.         self.old_stdout = os.dup(1)
  169.         self.old_stderr = os.dup(2)
  170.         os.dup2(self.logfd, 1)
  171.         os.dup2(self.logfd, 2)
  172.     def _stopAptResolverLog(self):
  173.         os.fsync(1)
  174.         os.fsync(2)
  175.         os.dup2(self.old_stdout, 1)
  176.         os.dup2(self.old_stderr, 2)
  177.     # use this decorator instead of the _start/_stop stuff directly
  178.     # FIXME: this should probably be a decorator class where all
  179.     #        logging is moved into?
  180.     def withResolverLog(f):
  181.         " decorator to ensure that the apt output is logged "
  182.         def wrapper(*args, **kwargs):
  183.             args[0]._startAptResolverLog()
  184.             res = f(*args, **kwargs)
  185.             args[0]._stopAptResolverLog()
  186.             return res
  187.         return wrapper
  188.  
  189.     # properties
  190.     @property
  191.     def requiredDownload(self):
  192.         """ get the size of the packages that are required to download """
  193.         pm = apt_pkg.GetPackageManager(self._depcache)
  194.         fetcher = apt_pkg.GetAcquire()
  195.         pm.GetArchives(fetcher, self._list, self._records)
  196.         return fetcher.FetchNeeded
  197.     @property
  198.     def additionalRequiredSpace(self):
  199.         """ get the size of the additional required space on the fs """
  200.         return self._depcache.UsrSize
  201.     @property
  202.     def isBroken(self):
  203.         """ is the cache broken """
  204.         return self._depcache.BrokenCount > 0
  205.  
  206.     # methods
  207.     def lockListsDir(self):
  208.         name = apt_pkg.Config.FindDir("Dir::State::Lists") + "lock"
  209.         self._listsLock = apt_pkg.GetLock(name)
  210.         if self._listsLock < 0:
  211.             e = "Can not lock '%s' " % name
  212.             raise CacheExceptionLockingFailed, e
  213.     def unlockListsDir(self):
  214.         if self._listsLock > 0:
  215.             os.close(self._listsLock)
  216.             self._listsLock = -1
  217.     def update(self, fprogress=None):
  218.         """
  219.         our own update implementation is required because we keep the lists
  220.         dir lock
  221.         """
  222.         self.unlockListsDir()
  223.         res = apt.Cache.update(self, fprogress)
  224.         self.lockListsDir()
  225.         if fprogress.release_file_download_error:
  226.             # FIXME: not ideal error message, but we just reuse a 
  227.             #        existing one here to avoid a new string
  228.             raise IOError(_("The server may be overloaded"))
  229.         if res == False:
  230.             raise IOError("apt.cache.update() returned False, but did not raise exception?!?")
  231.  
  232.     def commit(self, fprogress, iprogress):
  233.         logging.info("cache.commit()")
  234.         if self.lock:
  235.             self.releaseLock()
  236.         apt.Cache.commit(self, fprogress, iprogress)
  237.  
  238.     def releaseLock(self, pkgSystemOnly=True):
  239.         if self.lock:
  240.             try:
  241.                 apt_pkg.PkgSystemUnLock()
  242.                 self.lock = False
  243.             except SystemError, e:
  244.                 logging.debug("failed to SystemUnLock() (%s) " % e)
  245.  
  246.     def getLock(self, pkgSystemOnly=True):
  247.         if not self.lock:
  248.             try:
  249.                 apt_pkg.PkgSystemLock()
  250.                 self.lock = True
  251.             except SystemError, e:
  252.                 logging.debug("failed to SystemLock() (%s) " % e)
  253.  
  254.     def downloadable(self, pkg, useCandidate=True):
  255.         " check if the given pkg can be downloaded "
  256.         if useCandidate:
  257.             ver = self._depcache.GetCandidateVer(pkg._pkg)
  258.         else:
  259.             ver = pkg._pkg.CurrentVer
  260.         if ver == None:
  261.             logging.warning("no version information for '%s' (useCandidate=%s)" % (pkg.name, useCandidate))
  262.             return False
  263.         return ver.Downloadable
  264.     
  265.     def fixBroken(self):
  266.         """ try to fix broken dependencies on the system, may throw
  267.             SystemError when it can't"""
  268.         return self._depcache.FixBroken()
  269.  
  270.     def create_snapshot(self):
  271.         """ create a snapshot of the current changes """
  272.         self.to_install = []
  273.         self.to_remove = []
  274.         for pkg in self.getChanges():
  275.             if pkg.markedInstall or pkg.markedUpgrade:
  276.                 self.to_install.append(pkg.name)
  277.             if pkg.markedDelete:
  278.                 self.to_remove.append(pkg.name)
  279.  
  280.     def clear(self):
  281.         self._depcache.Init()
  282.  
  283.     def restore_snapshot(self):
  284.         """ restore a snapshot """
  285.         actiongroup = apt_pkg.GetPkgActionGroup(self._depcache)
  286.         self.clear()
  287.         for name in self.to_remove:
  288.             pkg = self[name]
  289.             pkg.markDelete()
  290.         for name in self.to_install:
  291.             pkg = self[name]
  292.             pkg.markInstall(autoFix=False, autoInst=False)
  293.  
  294.     def needServerMode(self):
  295.         """ 
  296.         This checks if we run on a desktop or a server install.
  297.         
  298.         A server install has more freedoms, for a desktop install
  299.         we force a desktop meta package to be install on the upgrade.
  300.  
  301.         We look for a installed desktop meta pkg and for key 
  302.         dependencies, if none of those are installed we assume
  303.         server mode
  304.         """
  305.         #logging.debug("needServerMode() run")
  306.         # check for the MetaPkgs (e.g. ubuntu-desktop)
  307.         metapkgs = self.config.getlist("Distro","MetaPkgs")
  308.         for key in metapkgs:
  309.             # if it is installed we are done
  310.             if self.has_key(key) and self[key].isInstalled:
  311.                 logging.debug("needServerMode(): run in 'desktop' mode, (because of pkg '%s')" % key)
  312.                 return False
  313.             # if it is not installed, but its key depends are installed 
  314.             # we are done too (we auto-select the package later)
  315.             deps_found = True
  316.             for pkg in self.config.getlist(key,"KeyDependencies"):
  317.                 deps_found &= self.has_key(pkg) and self[pkg].isInstalled
  318.             if deps_found:
  319.                 logging.debug("needServerMode(): run in 'desktop' mode, (because of key deps for '%s')" % key)
  320.                 return False
  321.         logging.debug("needServerMode(): can not find a desktop meta package or key deps, running in server mode")
  322.         return True
  323.  
  324.     def sanityCheck(self, view):
  325.         """ check if the cache is ok and if the required metapkgs
  326.             are installed
  327.         """
  328.         if self.isBroken:
  329.             try:
  330.                 logging.debug("Have broken pkgs, trying to fix them")
  331.                 self.fixBroken()
  332.             except SystemError:
  333.                 view.error(_("Broken packages"),
  334.                                  _("Your system contains broken packages "
  335.                                    "that couldn't be fixed with this "
  336.                                    "software. "
  337.                                    "Please fix them first using synaptic or "
  338.                                    "apt-get before proceeding."))
  339.                 return False
  340.         return True
  341.  
  342.     def markInstall(self, pkg, reason=""):
  343.         logging.debug("Installing '%s' (%s)" % (pkg, reason))
  344.         if self.has_key(pkg):
  345.             self[pkg].markInstall()
  346.             if not (self[pkg].markedInstall or self[pkg].markedUpgrade):
  347.                 logging.error("Installing/upgrading '%s' failed" % pkg)
  348.                 #raise (SystemError, "Installing '%s' failed" % pkg)
  349.                 return False
  350.         return True
  351.     def markUpgrade(self, pkg, reason=""):
  352.         logging.debug("Upgrading '%s' (%s)" % (pkg, reason))
  353.         if self.has_key(pkg) and self[pkg].isInstalled:
  354.             self[pkg].markUpgrade()
  355.             if not self[pkg].markedUpgrade:
  356.                 logging.error("Upgrading '%s' failed" % pkg)
  357.                 return False
  358.         return True
  359.     def markRemove(self, pkg, reason=""):
  360.         logging.debug("Removing '%s' (%s)" % (pkg, reason))
  361.         if self.has_key(pkg):
  362.             self[pkg].markDelete()
  363.     def markPurge(self, pkg, reason=""):
  364.         logging.debug("Purging '%s' (%s)" % (pkg, reason))
  365.         if self.has_key(pkg):
  366.             self._depcache.MarkDelete(self[pkg]._pkg,True)
  367.  
  368.     def _keepInstalled(self, pkgname, reason):
  369.         if (self.has_key(pkgname)
  370.             and self[pkgname].isInstalled
  371.             and self[pkgname].markedDelete):
  372.             self.markInstall(pkgname, reason)
  373.  
  374.     def keepInstalledRule(self):
  375.         """ run after the dist-upgrade to ensure that certain
  376.             packages are kept installed """
  377.         # first the global list
  378.         for pkgname in self.config.getlist("Distro","KeepInstalledPkgs"):
  379.             self._keepInstalled(pkgname, "Distro KeepInstalledPkgs rule")
  380.         # the the per-metapkg rules
  381.         for key in self.metapkgs:
  382.             if self.has_key(key) and (self[key].isInstalled or
  383.                                       self[key].markedInstall):
  384.                 for pkgname in self.config.getlist(key,"KeepInstalledPkgs"):
  385.                     self._keepInstalled(pkgname, "%s KeepInstalledPkgs rule" % key)
  386.  
  387.         # only enforce section if we have a network. Otherwise we run
  388.         # into CD upgrade issues for installed language packs etc
  389.         if self.config.get("Options","withNetwork") == "True":
  390.             logging.debug("Running KeepInstalledSection rules")
  391.             # now the KeepInstalledSection code
  392.             for section in self.config.getlist("Distro","KeepInstalledSection"):
  393.                 for pkg in self:
  394.                     if pkg.markedDelete and pkg.section == section:
  395.                         self._keepInstalled(pkg.name, "Distro KeepInstalledSection rule: %s" % section)
  396.             for key in self.metapkgs:
  397.                 if self.has_key(key) and (self[key].isInstalled or
  398.                                           self[key].markedInstall):
  399.                     for section in self.config.getlist(key,"KeepInstalledSection"):
  400.                         for pkg in self:
  401.                             if pkg.markedDelete and pkg.section == section:
  402.                                 self._keepInstalled(pkg.name, "%s KeepInstalledSection rule: %s" % (key, section))
  403.         
  404.  
  405.     def postUpgradeRule(self):
  406.         " run after the upgrade was done in the cache "
  407.         for (rule, action) in [("Install", self.markInstall),
  408.                                ("Upgrade", self.markUpgrade),
  409.                                ("Remove", self.markRemove),
  410.                                ("Purge", self.markPurge)]:
  411.             # first the global list
  412.             for pkg in self.config.getlist("Distro","PostUpgrade%s" % rule):
  413.                 action(pkg, "Distro PostUpgrade%s rule" % rule)
  414.             for key in self.metapkgs:
  415.                 if self.has_key(key) and (self[key].isInstalled or
  416.                                           self[key].markedInstall):
  417.                     for pkg in self.config.getlist(key,"PostUpgrade%s" % rule):
  418.                         action(pkg, "%s PostUpgrade%s rule" % (key, rule))
  419.         # run the quirks handlers
  420.         if not self.partialUpgrade:
  421.             self.quirks.run("PostDistUpgradeCache")
  422.  
  423.     def identifyObsoleteKernels(self):
  424.         # we have a funny policy that we remove security updates
  425.         # for the kernel from the archive again when a new ABI
  426.         # version hits the archive. this means that we have
  427.         # e.g. 
  428.         # linux-image-2.6.24-15-generic 
  429.         # is obsolete when 
  430.         # linux-image-2.6.24-19-generic
  431.         # is available
  432.         # ...
  433.         # This code tries to identify the kernels that can be removed
  434.         logging.debug("identifyObsoleteKernels()")
  435.         obsolete_kernels = set()
  436.         version = self.config.get("KernelRemoval","Version")
  437.         basenames = self.config.getlist("KernelRemoval","BaseNames")
  438.         types = self.config.getlist("KernelRemoval","Types")
  439.         for pkg in self:
  440.             for base in basenames:
  441.                 basename = "%s-%s-" % (base,version)
  442.                 for type in types:
  443.                     if (pkg.name.startswith(basename) and 
  444.                         pkg.name.endswith(type) and
  445.                         pkg.isInstalled):
  446.                         if (pkg.name == "%s-%s" % (base,self.uname)):
  447.                             logging.debug("skipping running kernel %s" % pkg.name)
  448.                             continue
  449.                         logging.debug("removing obsolete kernel '%s'" % pkg.name)
  450.                         obsolete_kernels.add(pkg.name)
  451.         logging.debug("identifyObsoleteKernels found '%s'" % obsolete_kernels)
  452.         return obsolete_kernels
  453.  
  454.     def checkForNvidia(self):
  455.         """ 
  456.         this checks for nvidia hardware and checks what driver is needed
  457.         """
  458.         logging.debug("nvidiaUpdate()")
  459.         # if the free drivers would give us a equally hard time, we would
  460.         # never be able to release
  461.         try:
  462.             from NvidiaDetector.nvidiadetector import NvidiaDetection
  463.         except ImportError, e:
  464.             logging.error("NvidiaDetector can not be imported %s" % e)
  465.             return False
  466.         try:
  467.             # get new detection module and use the modalises files
  468.             # from within the release-upgrader
  469.             nv = NvidiaDetection(datadir="./modaliases/")
  470.             #nv = NvidiaDetection()
  471.             # check if a binary driver is installed now
  472.             for oldDriver in nv.oldPackages:
  473.                 if self.has_key(oldDriver) and self[oldDriver].isInstalled:
  474.                     self.markRemove(oldDriver, "old nvidia driver")
  475.                     break
  476.             else:
  477.                 logging.info("no old nvidia driver installed, installing no new")
  478.                 return False
  479.             # check which one to use
  480.             driver = nv.selectDriver()
  481.             logging.debug("nv.selectDriver() returned '%s'" % driver)
  482.             if not self.has_key(driver):
  483.                 logging.warning("no '%s' found" % driver)
  484.                 return False
  485.             if not (self[driver].markedInstall or self[driver].markedUpgrade):
  486.                 self[driver].markInstall()
  487.                 logging.info("installing %s as suggested by NvidiaDetector" % driver)
  488.                 return True
  489.         except Exception, e:
  490.             logging.error("NvidiaDetection returned a error: %s" % e)
  491.         return False
  492.  
  493.  
  494.     def getKernelsFromBaseInstaller(self):
  495.         """get the list of recommended kernels from base-installer"""
  496.         kernels = Popen(["/bin/sh", "./get_kernel_list.sh"],
  497.                         stdout=PIPE).communicate()[0]
  498.         kernels = filter(lambda x : len(x) > 0,
  499.                          map(string.strip, kernels.split("\n")))
  500.         logging.debug("./get_kernel_list.sh returns: %s" % kernels)
  501.         return kernels
  502.  
  503.     def _selectKernelFromBaseInstaller(self):
  504.         """ use the get_kernel_list.sh script (that uses base-installer)
  505.             to figure out what kernel is most suitable for the system
  506.         """
  507.         # check if we have a kernel from that list installed first
  508.         kernels = self.getKernelsFromBaseInstaller()
  509.         for kernel in kernels:
  510.             if not self.has_key(kernel):
  511.                 logging.debug("%s not available in cache" % kernel)
  512.                 continue
  513.             # check if installed
  514.             if self[kernel].isInstalled:
  515.                 logging.debug("%s kernel already installed" % kernel)
  516.                 if self[kernel].isUpgradable and not self[kernel].markedUpgrade:
  517.                     self.markUpgrade(kernel, "Upgrading kernel from base-installer")
  518.                 return 
  519.         # if we have not found a kernel yet, use the first one that installs
  520.         for kernel in kernels:
  521.             if self.markInstall(kernel, "Selecting new kernel from base-installer"):
  522.                 return
  523.  
  524.     def checkForKernel(self):
  525.         """ check for the running kernel and try to ensure that we have
  526.             an updated version
  527.         """
  528.         logging.debug("Kernel uname: '%s' " % self.uname)
  529.         try:
  530.             (version, build, flavour) = self.uname.split("-")
  531.         except Exception, e:
  532.             logging.warning("Can't parse kernel uname: '%s' (self compiled?)" % e)
  533.             return False
  534.         # now check if we have a SMP system
  535.         dmesg = Popen(["dmesg"],stdout=PIPE).communicate()[0]
  536.         if "WARNING: NR_CPUS limit" in dmesg:
  537.             logging.debug("UP kernel on SMP system!?!")
  538.         # use base-installer to get the kernel we want (if it exists)
  539.         if os.path.exists("./get_kernel_list.sh"):
  540.             self._selectKernelFromBaseInstaller()
  541.         else:
  542.             logging.debug("skipping ./get_kernel_list.sh: not found")
  543.         return True
  544.  
  545.     def checkPriority(self):
  546.         # tuple of priorities we require to be installed 
  547.         need = ('required', )
  548.         # stuff that its ok not to have
  549.         removeEssentialOk = self.config.getlist("Distro","RemoveEssentialOk")
  550.         # check now
  551.         for pkg in self:
  552.             # WORKADOUND bug on the CD/python-apt #253255
  553.             ver = pkg._pcache._depcache.GetCandidateVer(pkg._pkg)
  554.             if ver and ver.Priority == 0:
  555.                 logging.error("Package %s has no priority set" % pkg.name)
  556.                 continue
  557.             if (pkg.candidateDownloadable and
  558.                 not (pkg.isInstalled or pkg.markedInstall) and
  559.                 not pkg.name in removeEssentialOk and
  560.                 pkg.priority in need):
  561.                 self.markInstall(pkg.name, "priority in required set '%s' but not scheduled for install" % need)
  562.  
  563.     # FIXME: make this a decorator (just like the withResolverLog())
  564.     def updateGUI(self, view, lock):
  565.         while lock.locked():
  566.             view.processEvents()
  567.             time.sleep(0.01)
  568.  
  569.     @withResolverLog
  570.     def distUpgrade(self, view, serverMode, partialUpgrade):
  571.         # keep the GUI alive
  572.         lock = threading.Lock()
  573.         lock.acquire()
  574.         t = threading.Thread(target=self.updateGUI, args=(self.view, lock,))
  575.         t.start()
  576.         try:
  577.             # upgrade (and make sure this way that the cache is ok)
  578.             self.upgrade(True)
  579.  
  580.             # check that everything in priority required is installed
  581.             self.checkPriority()
  582.  
  583.             # see if our KeepInstalled rules are honored
  584.             self.keepInstalledRule()
  585.  
  586.             # check if we got a new kernel
  587.             self.checkForKernel()
  588.  
  589.             # check for nvidia stuff
  590.             self.checkForNvidia()
  591.  
  592.             # and if we have some special rules
  593.             self.postUpgradeRule()
  594.  
  595.             # install missing meta-packages (if not in server upgrade mode)
  596.             self._keepBaseMetaPkgsInstalled(view)
  597.             if not serverMode:
  598.                 # if this fails, a system error is raised
  599.                 self._installMetaPkgs(view)
  600.  
  601.             # see if it all makes sense, if not this function raises 
  602.             self._verifyChanges()
  603.  
  604.         except SystemError, e:
  605.             # this should go into a finally: line, see below for the 
  606.             # rationale why it doesn't 
  607.             lock.release()
  608.             t.join()
  609.             # FIXME: change the text to something more useful
  610.             details =  _("An unresolvable problem occurred while "
  611.                          "calculating the upgrade:\n%s\n\n "
  612.                          "This can be caused by:\n"
  613.                          " * Upgrading to a pre-release version of Ubuntu\n"
  614.                          " * Running the current pre-release version of Ubuntu\n"
  615.                          " * Unofficial software packages not provided by Ubuntu\n"
  616.                          "\n" % e)
  617.             # we never have partialUpgrades (including removes) on a stable system
  618.             # with only ubuntu sources so we do not recommend reporting a bug
  619.             if partialUpgrade:
  620.                 details += _("This is most likely a transient problem, "
  621.                              "please try again later.")
  622.             else:
  623.                 details += _("If none of this applies, then please report this bug against "
  624.                              "the 'update-manager' package and include the files in "
  625.                              "/var/log/dist-upgrade/ in the bug report.")
  626.             # make the error text available again on stdout for the 
  627.             # text frontend
  628.             self._stopAptResolverLog()
  629.             view.error(_("Could not calculate the upgrade"), details)
  630.             # start the resolver log again because this is run with
  631.             # the withResolverLog decorator
  632.             self._startAptResolverLog()            
  633.             logging.error("Dist-upgrade failed: '%s'", e)
  634.             return False
  635.         # would be nice to be able to use finally: here, but we need
  636.         # to run on python2.4 too 
  637.         #finally:
  638.         # wait for the gui-update thread to exit
  639.         lock.release()
  640.         t.join()
  641.         
  642.         # check the trust of the packages that are going to change
  643.         untrusted = []
  644.         for pkg in self.getChanges():
  645.             if pkg.markedDelete:
  646.                 continue
  647.             # special case because of a bug in pkg.candidateOrigin
  648.             if pkg.markedDowngrade:
  649.                 for ver in pkg._pkg.VersionList:
  650.                     # version is lower than installed one
  651.                     if apt_pkg.VersionCompare(ver.VerStr, pkg.installedVersion) < 0:
  652.                         for (verFileIter,index) in ver.FileList:
  653.                             indexfile = pkg._list.FindIndex(verFileIter)
  654.                             if indexfile and not indexfile.IsTrusted:
  655.                                 untrusted.append(pkg.name)
  656.                                 break
  657.                 continue
  658.             origins = pkg.candidateOrigin
  659.             trusted = False
  660.             for origin in origins:
  661.                 #print origin
  662.                 trusted |= origin.trusted
  663.             if not trusted:
  664.                 untrusted.append(pkg.name)
  665.         # check if the user overwrote the unauthenticated warning
  666.         try:
  667.             b = self.config.getboolean("Distro","AllowUnauthenticated")
  668.             if b:
  669.                 logging.warning("AllowUnauthenticated set!")
  670.                 return True
  671.         except ConfigParser.NoOptionError, e:
  672.             pass
  673.         if len(untrusted) > 0:
  674.             untrusted.sort()
  675.             logging.error("Unauthenticated packages found: '%s'" % \
  676.                           " ".join(untrusted))
  677.             # FIXME: maybe ask a question here? instead of failing?
  678.             self._stopAptResolverLog()
  679.             view.error(_("Error authenticating some packages"),
  680.                        _("It was not possible to authenticate some "
  681.                          "packages. This may be a transient network problem. "
  682.                          "You may want to try again later. See below for a "
  683.                          "list of unauthenticated packages."),
  684.                        "\n".join(untrusted))
  685.             # start the resolver log again because this is run with
  686.             # the withResolverLog decorator
  687.             self._startAptResolverLog()            
  688.             return False
  689.         return True
  690.  
  691.     def _verifyChanges(self):
  692.         """ this function tests if the current changes don't violate
  693.             our constrains (blacklisted removals etc)
  694.         """
  695.         removeEssentialOk = self.config.getlist("Distro","RemoveEssentialOk")
  696.         # check changes
  697.         for pkg in self.getChanges():
  698.             if pkg.markedDelete and self._inRemovalBlacklist(pkg.name):
  699.                 logging.debug("The package '%s' is marked for removal but it's in the removal blacklist", pkg.name)
  700.                 raise SystemError, _("The package '%s' is marked for removal but it is in the removal blacklist.") % pkg.name
  701.             if pkg.markedDelete and (pkg._pkg.Essential == True and
  702.                                      not pkg.name in removeEssentialOk):
  703.                 logging.debug("The package '%s' is marked for removal but it's a ESSENTIAL package", pkg.name)
  704.                 raise SystemError, _("The essential package '%s' is marked for removal.") % pkg.name
  705.         # check bad-versions blacklist
  706.         badVersions = self.config.getlist("Distro","BadVersions")
  707.         for bv in badVersions:
  708.             (pkgname, ver) = bv.split("_")
  709.             if (self.has_key(pkgname) and
  710.                 self[pkgname].candidateVersion == ver and
  711.                 (self[pkgname].markedInstall or
  712.                  self[pkgname].markedUpgrade)):
  713.                 raise SystemError, _("Trying to install blacklisted version '%s'") % bv
  714.         return True
  715.     
  716.     def _lookupPkgRecord(self, pkg):
  717.         """ 
  718.         helper to make sure that the pkg._records is pointing to the right
  719.         location - needed because python-apt 0.7.9 dropped the python-apt
  720.         version but we can not yet use the new version because on upgrade
  721.         the old version is still installed
  722.         """ 
  723.         ver = pkg._pcache._depcache.GetCandidateVer(pkg._pkg)
  724.         if ver is None:
  725.             print "No candidate ver: ", pkg.name
  726.             return False
  727.         if ver.FileList is None:
  728.             print "No FileList for: %s " % self._pkg.Name()
  729.             return False
  730.         f, index = ver.FileList.pop(0)
  731.         pkg._pcache._records.Lookup((f, index))
  732.         return True
  733.  
  734.     @property
  735.     def installedTasks(self):
  736.         tasks = {}
  737.         installed_tasks = set()
  738.         for pkg in self:
  739.             if not self._lookupPkgRecord(pkg):
  740.                 logging.debug("no PkgRecord found for '%s', skipping " % pkg.name)
  741.                 continue
  742.             for line in pkg._pcache._records.Record.split("\n"):
  743.                 if line.startswith("Task:"):
  744.                     for task in (line[len("Task:"):]).split(","):
  745.                         task = task.strip()
  746.                         if not tasks.has_key(task):
  747.                             tasks[task] = set()
  748.                         tasks[task].add(pkg.name)
  749.         for task in tasks:
  750.             installed = True
  751.             for pkgname in tasks[task]:
  752.                 if not self[pkgname].isInstalled:
  753.                     installed = False
  754.                     break
  755.             if installed:
  756.                 installed_tasks.add(task)
  757.         return installed_tasks
  758.             
  759.     def installTasks(self, tasks):
  760.         logging.debug("running installTasks")
  761.         for pkg in self:
  762.             if pkg.markedInstall or pkg.isInstalled:
  763.                 continue
  764.             self._lookupPkgRecord(pkg)
  765.             if not (hasattr(pkg._pcache._records,"Record") and pkg._pcache._records.Record):
  766.                 logging.warning("can not find Record for '%s'" % pkg.name)
  767.                 continue
  768.             for line in pkg._pcache._records.Record.split("\n"):
  769.                 if line.startswith("Task:"):
  770.                     for task in (line[len("Task:"):]).split(","):
  771.                         task = task.strip()
  772.                         if task in tasks:
  773.                             pkg.markInstall()
  774.         return True
  775.     
  776.     def _keepBaseMetaPkgsInstalled(self, view):
  777.         for pkg in self.config.getlist("Distro","BaseMetaPkgs"):
  778.             self._keepInstalled(pkg, "base meta package keep installed rule")
  779.  
  780.     def _installMetaPkgs(self, view):
  781.  
  782.         def metaPkgInstalled():
  783.             """ 
  784.             internal helper that checks if at least one meta-pkg is 
  785.             installed or marked install
  786.             """
  787.             for key in metapkgs:
  788.                 if self.has_key(key):
  789.                     pkg = self[key]
  790.                     if pkg.isInstalled and pkg.markedDelete:
  791.                         logging.debug("metapkg '%s' installed but markedDelete" % pkg.name)
  792.                     if ((pkg.isInstalled and not pkg.markedDelete) 
  793.                         or self[key].markedInstall):
  794.                         return True
  795.             return False
  796.  
  797.         # now check for ubuntu-desktop, kubuntu-desktop, edubuntu-desktop
  798.         metapkgs = self.config.getlist("Distro","MetaPkgs")
  799.  
  800.         # we never go without ubuntu-base
  801.         for pkg in self.config.getlist("Distro","BaseMetaPkgs"):
  802.             self[pkg].markInstall()
  803.  
  804.         # every meta-pkg that is installed currently, will be marked
  805.         # install (that result in a upgrade and removes a markDelete)
  806.         for key in metapkgs:
  807.             try:
  808.                 if self.has_key(key) and self[key].isInstalled:
  809.                     logging.debug("Marking '%s' for upgrade" % key)
  810.                     self[key].markUpgrade()
  811.             except SystemError, e:
  812.                 logging.debug("Can't mark '%s' for upgrade (%s)" % (key,e))
  813.                 raise SystemError, _("Can not mark '%s' for upgrade") % key
  814.         # check if we have a meta-pkg, if not, try to guess which one to pick
  815.         if not metaPkgInstalled():
  816.             logging.debug("none of the '%s' meta-pkgs installed" % metapkgs)
  817.             for key in metapkgs:
  818.                 deps_found = True
  819.                 for pkg in self.config.getlist(key,"KeyDependencies"):
  820.                     deps_found &= self.has_key(pkg) and self[pkg].isInstalled
  821.                 if deps_found:
  822.                     logging.debug("guessing '%s' as missing meta-pkg" % key)
  823.                     try:
  824.                         self[key].markInstall()
  825.                     except (SystemError, KeyError), e:
  826.                         logging.error("failed to mark '%s' for install (%s)" % (key,e))
  827.                         view.error(_("Can't install '%s'" % key),
  828.                                    _("It was impossible to install a "
  829.                                      "required package. Please report "
  830.                                      "this as a bug. "))
  831.                         return False
  832.                     logging.debug("markedInstall: '%s' -> '%s'" % (key, self[key].markedInstall))
  833.                     break
  834.         # check if we actually found one
  835.         if not metaPkgInstalled():
  836.             # FIXME: provide a list
  837.             view.error(_("Can't guess meta-package"),
  838.                        _("Your system does not contain a "
  839.                          "ubuntu-desktop, kubuntu-desktop, xubuntu-desktop or "
  840.                          "edubuntu-desktop package and it was not "
  841.                          "possible to detect which version of "
  842.                         "Ubuntu you are running.\n "
  843.                          "Please install one of the packages "
  844.                          "above first using synaptic or "
  845.                          "apt-get before proceeding."))
  846.             return False
  847.         return True
  848.  
  849.     def _inRemovalBlacklist(self, pkgname):
  850.         for expr in self.removal_blacklist:
  851.             if re.compile(expr).match(pkgname):
  852.                 return True
  853.         return False
  854.  
  855.     @withResolverLog
  856.     def tryMarkObsoleteForRemoval(self, pkgname, remove_candidates, foreign_pkgs):
  857.         #logging.debug("tryMarkObsoleteForRemoval(): %s" % pkgname)
  858.         # sanity check, first see if it looks like a running kernel pkg
  859.         if pkgname.endswith(self.uname):
  860.             logging.debug("skipping running kernel pkg '%s'" % pkgname)
  861.             return False
  862.         if self._inRemovalBlacklist(pkgname):
  863.             logging.debug("skipping '%s' (in removalBlacklist)" % pkgname)
  864.             return False
  865.         # ensure we honor KeepInstalledSection here as well
  866.         for section in self.config.getlist("Distro","KeepInstalledSection"):
  867.             if self.has_key(pkgname) and self[pkgname].section == section:
  868.                 logging.debug("skipping '%s' (in KeepInstalledSection)" % pkgname)
  869.                 return False
  870.         # if we don't have the package anyway, we are fine (this can
  871.         # happen when forced_obsoletes are specified in the config file)
  872.         if not self.has_key(pkgname):
  873.             #logging.debug("package '%s' not in cache" % pkgname)
  874.             return True
  875.         # check if we want to purge 
  876.         try:
  877.             purge = self.config.getboolean("Distro","PurgeObsoletes")
  878.         except ConfigParser.NoOptionError, e:
  879.             purge = False
  880.  
  881.         # this is a delete candidate, only actually delete,
  882.         # if it dosn't remove other packages depending on it
  883.         # that are not obsolete as well
  884.         actiongroup = apt_pkg.GetPkgActionGroup(self._depcache)
  885.         self.create_snapshot()
  886.         try:
  887.             self[pkgname].markDelete(purge=purge)
  888.             self.view.processEvents()
  889.             #logging.debug("marking '%s' for removal" % pkgname)
  890.             for pkg in self.getChanges():
  891.                 if (pkg.name not in remove_candidates or 
  892.                       pkg.name in foreign_pkgs or 
  893.                       self._inRemovalBlacklist(pkg.name)):
  894.                     logging.debug("package '%s' has unwanted removals, skipping" % pkgname)
  895.                     self.restore_snapshot()
  896.                     return False
  897.         except (SystemError,KeyError),e:
  898.             logging.warning("_tryMarkObsoleteForRemoval failed for '%s' (%s: %s)" % (pkgname, repr(e), e))
  899.             self.restore_snapshot()
  900.             return False
  901.         return True
  902.     
  903.     def _getObsoletesPkgs(self):
  904.         " get all package names that are not downloadable "
  905.         obsolete_pkgs =set()        
  906.         for pkg in self:
  907.             if pkg.isInstalled: 
  908.                 # check if any version is downloadable. we need to check
  909.                 # for older ones too, because there might be
  910.                 # cases where e.g. firefox in gutsy-updates is newer
  911.                 # than hardy
  912.                 if not self.anyVersionDownloadable(pkg):
  913.                     obsolete_pkgs.add(pkg.name)
  914.         return obsolete_pkgs
  915.  
  916.     def anyVersionDownloadable(self, pkg):
  917.         " helper that checks if any of the version of pkg is downloadable "
  918.         for ver in pkg._pkg.VersionList:
  919.             if ver.Downloadable:
  920.                 return True
  921.         return False
  922.  
  923.     def _getUnusedDependencies(self):
  924.         " get all package names that are not downloadable "
  925.         unused_dependencies =set()        
  926.         for pkg in self:
  927.             if pkg.isInstalled and self._depcache.IsGarbage(pkg._pkg):
  928.                 unused_dependencies.add(pkg.name)
  929.         return unused_dependencies
  930.  
  931.     def _getForeignPkgs(self, allowed_origin, fromDist, toDist):
  932.         """ get all packages that are installed from a foreign repo
  933.             (and are actually downloadable)
  934.         """
  935.         foreign_pkgs=set()        
  936.         for pkg in self:
  937.             if pkg.isInstalled and self.downloadable(pkg):
  938.                 # assume it is foreign and see if it is from the 
  939.                 # official archive
  940.                 foreign=True
  941.                 for origin in pkg.candidateOrigin:
  942.                     # FIXME: use some better metric here
  943.                     if fromDist in origin.archive and \
  944.                            origin.origin == allowed_origin:
  945.                         foreign = False
  946.                     if toDist in origin.archive and \
  947.                            origin.origin == allowed_origin:
  948.                         foreign = False
  949.                 if foreign:
  950.                     foreign_pkgs.add(pkg.name)
  951.         return foreign_pkgs
  952.  
  953.     def checkFreeSpace(self):
  954.         """
  955.         this checks if we have enough free space on /var, /boot and /usr
  956.         with the given cache 
  957.  
  958.         Note: this can not be fully accurate if there are multiple 
  959.               mountpoints for /usr, /var, /boot
  960.         """
  961.  
  962.         class FreeSpace(object):
  963.             " helper class that represents the free space on each mounted fs "
  964.             def __init__(self, initialFree):
  965.                 self.free = initialFree
  966.                 self.need = 0
  967.  
  968.         def make_fs_id(d):
  969.             """ return 'id' of a directory so that directories on the
  970.                 same filesystem get the same id (simply the mount_point)
  971.             """
  972.             for mount_point in mounted:
  973.                 if d.startswith(mount_point):
  974.                     return mount_point
  975.             return "/"
  976.  
  977.         # this is all a bit complicated
  978.         # 1) check what is mounted (in mounted)
  979.         # 2) create FreeSpace objects for the dirs we are interested in
  980.         #    (mnt_map)
  981.         # 3) use the  mnt_map to check if we have enough free space and
  982.         #    if not tell the user how much is missing
  983.         mounted = []
  984.         mnt_map = {}
  985.         fs_free = {}
  986.         for line in open("/proc/mounts"):
  987.             try:
  988.                 (what, where, fs, options, a, b) = line.split()
  989.             except ValueError, e:
  990.                 logging.debug("line '%s' in /proc/mounts not understood (%s)" % (line, e))
  991.                 continue
  992.             if not where in mounted:
  993.                 mounted.append(where)
  994.         # make sure mounted is sorted by longest path
  995.         mounted.sort(cmp=lambda a,b: cmp(len(a),len(b)), reverse=True)
  996.         archivedir = apt_pkg.Config.FindDir("Dir::Cache::archives")
  997.         aufs_rw_dir = "/tmp/"
  998.         if (hasattr(self, "config") and
  999.             self.config.getWithDefault("Aufs","Enabled", False)):
  1000.             aufs_rw_dir = self.config.get("Aufs","RWDir")
  1001.             if not os.path.exists(aufs_rw_dir):
  1002.                 os.makedirs(aufs_rw_dir)
  1003.         logging.debug("cache aufs_rw_dir: %s" % aufs_rw_dir)
  1004.         for d in ["/","/usr","/var","/boot", archivedir, aufs_rw_dir, "/home","/tmp/"]:
  1005.             d = os.path.realpath(d)
  1006.             fs_id = make_fs_id(d)
  1007.             st = os.statvfs(d)
  1008.             free = st[statvfs.F_BAVAIL]*st[statvfs.F_FRSIZE]
  1009.             if fs_id in mnt_map:
  1010.                 logging.debug("Dir %s mounted on %s" % (d,mnt_map[fs_id]))
  1011.                 fs_free[d] = fs_free[mnt_map[fs_id]]
  1012.             else:
  1013.                 logging.debug("Free space on %s: %s" % (d,free))
  1014.                 mnt_map[fs_id] = d
  1015.                 fs_free[d] = FreeSpace(free)
  1016.         del mnt_map
  1017.         logging.debug("fs_free contains: '%s'" % fs_free)
  1018.  
  1019.         # now calculate the space that is required on /boot
  1020.         # we do this by checking how many linux-image-$ver packages
  1021.         # are installed or going to be installed
  1022.         space_in_boot = 0
  1023.         for pkg in self:
  1024.             # we match against everything that looks like a kernel
  1025.             # and add space check to filter out metapackages
  1026.             if re.match("^linux-(image|image-debug)-[0-9.]*-.*", pkg.name):
  1027.                 if pkg.markedInstall:
  1028.                     logging.debug("%s (new-install) added with %s to boot space" % (pkg.name, KERNEL_INITRD_SIZE))
  1029.                     space_in_boot += KERNEL_INITRD_SIZE
  1030.                 # mvo: jaunty does not create .bak files anymore
  1031.                 #elif (pkg.markedUpgrade or pkg.isInstalled):
  1032.                 #    logging.debug("%s (upgrade|installed) added with %s to boot space" % (pkg.name, KERNEL_INITRD_SIZE))
  1033.                 #    space_in_boot += KERNEL_INITRD_SIZE # creates .bak
  1034.  
  1035.         # we check for various sizes:
  1036.         # archivedir is were we download the debs
  1037.         # /usr is assumed to get *all* of the install space (incorrect,
  1038.         #      but as good as we can do currently + safety buffer
  1039.         # /     has a small safety buffer as well
  1040.         required_for_aufs = 0.0
  1041.         if (hasattr(self, "config") and
  1042.             self.config.getWithDefault("Aufs","Enabled", False)):
  1043.             logging.debug("taking aufs overlay into space calculation")
  1044.             aufs_rw_dir = self.config.get("Aufs","RWDir")
  1045.             # if we use the aufs rw overlay all the space is consumed
  1046.             # the overlay dir
  1047.             for pkg in self:
  1048.                 if pkg.markedUpgrade or pkg.markedInstall:
  1049.                     required_for_aufs += self._depcache.GetCandidateVer(pkg._pkg).Size
  1050.                     
  1051.         # sum up space requirements
  1052.         for (dir, size) in [(archivedir, self.requiredDownload),
  1053.                             # plus 50M safety buffer in /usr
  1054.                             ("/usr", self.additionalRequiredSpace),
  1055.                             ("/usr", 50*1024*1024),
  1056.                             ("/boot", space_in_boot), 
  1057.                             ("/tmp", 5*1024*1024),   # /tmp for dkms LP: #427035
  1058.                             ("/", 10*1024*1024),     # small safety buffer /
  1059.                             (aufs_rw_dir, required_for_aufs),
  1060.                            ]:
  1061.             dir = os.path.realpath(dir)
  1062.             logging.debug("dir '%s' needs '%s' of '%s' (%f)" % (dir, size, fs_free[dir], fs_free[dir].free))
  1063.             fs_free[dir].free -= size
  1064.             fs_free[dir].need += size
  1065.  
  1066.         # check for space required violations
  1067.         required_list = {}
  1068.         for dir in fs_free:
  1069.             if fs_free[dir].free < 0:
  1070.                 free_at_least = apt_pkg.SizeToStr(float(abs(fs_free[dir].free)+1))
  1071.                 # make_fs_id ensures we only get stuff on the same
  1072.                 # mountpoint, so we report the requirements only once
  1073.                 # per mountpoint
  1074.                 required_list[make_fs_id(dir)] = FreeSpaceRequired(apt_pkg.SizeToStr(fs_free[dir].need), make_fs_id(dir), free_at_least)
  1075.         # raise exception if free space check fails
  1076.         if len(required_list) > 0:
  1077.             logging.error("Not enough free space: %s" % [str(i) for i in required_list])
  1078.             raise NotEnoughFreeSpaceError(required_list.values())
  1079.         return True
  1080.  
  1081.  
  1082.  
  1083. if __name__ == "__main__":
  1084.     import DistUpgradeConfigParser
  1085.         import DistUpgradeView
  1086.         print "foo"
  1087.     c = MyCache(DistUpgradeConfigParser.DistUpgradeConfig("."),
  1088.                     DistUpgradeView.DistUpgradeView(), None)
  1089.         #c.checkForNvidia()
  1090.         #print c._identifyObsoleteKernels()
  1091.         print c.checkFreeSpace()
  1092.         sys.exit()
  1093.     c.clear()
  1094.         c.create_snapshot()
  1095.         c.installedTasks
  1096.         c.installTasks(["ubuntu-desktop"])
  1097.         print c.getChanges()
  1098.         c.restore_snapshot()
  1099.